
const fs = require('fs')
const process = require("child_process");
const { encode } = require('js-base64');
const { v4 } = require('uuid');
const path = require('path');
const db = require('db');
const moment = require('moment');
const evaluate = require('tools/evaluate');
const log = require('tools/log');

module.exports = async ctx => {
    const { connection, run, comply } = db.init();
    await run('BEGIN'); // 开始事务
    try {
        ctx.request.body.values.fileName = ctx.request.body.values.fileName && ctx.request.body.values.fileName.replace(/\s*/g, "");
        const order = ctx.request.body.order;
        const fv4 = v4();
        const settlement = await createSettlement(ctx, order, fv4, run);
        await updateEntry(order, settlement, comply);
        await setOrderStatus(order, comply);
        await setOrderAmount(order, comply);
        await rename(ctx, fv4);
        await run('COMMIT');
        ctx.status = 200;
        ctx.body = 'ok';
    } catch (error) {
        await run('ROLLBACK');
        ctx.status = 500;
        ctx.body = error;
        log('---------------------------------------------------')
        console.log(error)
    }
    await execSync(`rm ${ctx.request.body.values.filePath}`);
    connection.end();
}

/** 创建结算单 */
async function createSettlement(ctx, order, fv4, run) {
    const values = {
        ...ctx.request.body.values,
        settlementState: '待审核',
        fileName: undefined,
        filePath: undefined,
    };

    const totalAmount = evaluate(order.map(e => e.toBeSettled).join('+'))
    if (`${totalAmount}` === `NaN`) {
        throw { sqlMessage: '请输入结算金额' }
    }

    const commercialSpecialistId = order[0].commercialSpecialistId;
    const commercialSpecialistName = order[0].commercialSpecialistName;
    const settlementDetails = ctx.request.body.values.fileName && encode(path.join(getNewPath(fv4), ctx.request.body.values.fileName));

    let result = await run(`INSERT INTO settlement ${toInsert({
        ...values,
        commercialSpecialistId,
        totalAmount,
        commercialSpecialistName,
        settlementDetails
    }, { createTime: 'now()' })}`)
    if (result.err) {
        throw result.err
    }

    result = await run('SELECT LAST_INSERT_ID() as id');
    if (result.err) { throw result.err }

    const id = result.rows[0].id;
    return { id, ...values }
}

/** 更新 结算条目 结算单id */
async function updateEntry(order, settlement, comply) {
    for (let i = 0; i < order.length; i++) {

        const it = order[i];
        const unsettlementAmount = (await comply(`SELECT * FROM settlementEntry WHERE ordersId=${it.id} and settlementId is Null`))[0].unsettlementAmount;

        // 是否需要拆分
        if (unsettlementAmount > it.toBeSettled) {
            await comply(`
                UPDATE settlementEntry 
                SET settlementId=${settlement.id}, unsettlementAmount=${it.toBeSettled} 
                WHERE ordersId=${it.id} and settlementId is Null
            `);

            await comply(`
                INSERT INTO settlementEntry 
                ${toInsert({
                ordersId: it.id,
                unsettlementAmount: evaluate(unsettlementAmount + '-' + it.toBeSettled)
            })}
            `);
        // } else if (unsettlementAmount === it.toBeSettled) {
        } else {
            await comply(`
                UPDATE settlementEntry 
                SET settlementId=${settlement.id}, unsettlementAmount=${it.toBeSettled}  
                WHERE ordersId=${it.id} and settlementId is Null
            `)
        } 
        // else {
        //     throw { sqlMessage: '数据错误' }
        // }
    }
}

/** 更新订单状态到“结算中” */
async function setOrderStatus(order, comply) {
    const sqlStr = `
        UPDATE orders SET ${toUpdate({ orderStatus: '结算中' })}
        WHERE ${order.map(e => `id=${e.id}`).join(' OR ')}
    `

    result = await comply(sqlStr);
}

/** 更新订单的结算金额 */
async function setOrderAmount(order, comply) {
    order.forEach(async e => {
        const total = (await comply(`
            SELECT 
                SUM(unsettlementAmount) as total 
            FROM 
		        settlementEntry
            LEFT JOIN
		        settlement ON settlement.id = settlementEntry.settlementId
            WHERE 
                settlementId IS NOT NULL AND 
                ordersId = ${e.ordersId} AND
                settlement.isReceipt != '否'
        `))[0].total;

        await comply(`
            UPDATE 
                orders 
            SET 
                settlementAmount=${total} 
            WHERE 
                id=${e.ordersId}
        `)
    })
}

/** 移动结算明细文件 */
async function rename(ctx, fv4) {
    try {
        if (!ctx.request.body.values.fileName) return;
        const fileName = ctx.request.body.values.fileName;
        const filePath = ctx.request.body.values.filePath;

        const files = '../../../../../../files';
        const newPath = getNewPath(fv4);
        await execSync(`mkdir -p ${path.join(__dirname, files, newPath)}`);
        fs.renameSync(filePath, path.join(__dirname, files, newPath, fileName))
    } catch (error) {
        log('---------------------------------------------------')
        console.log(error)
    }
}

// ----------------------------------------------------------------------

/** 输入 更新 （UPDATE） 字符串 */
function toUpdate(body) {
    const keys = []
    for (key in body) {
        keys.push(`${key}=${typeof body[key] === 'string' ? `'${body[key]}'` : body[key]}`);
    }
    const sql = `${keys.join(',')}`;
    return sql;
}

/** 输出 添加 (INSERT) 字符串 */
function toInsert(value, affix) {
    const keys = [];
    const vals = [];

    for (let key in value) {
        if (value[key]) {
            keys.push(key);
            vals.push(typeof value[key] === 'string' ? `'${value[key]}'` : value[key])
        }
    }

    for (let key in affix) {
        if (affix[key]) {
            keys.push(key);
            vals.push(affix[key])
        }
    }

    return `(${keys.join(',')}) VALUES (${vals.join(',')})`
}

/** 获取一个新路径 */
function getNewPath(fv4) {
    return path.join(moment().format('YYYY_MM'), 'settlement', fv4);
}

/** 执行 sh 脚本 */
function execSync(command) {
    return new Promise(resolve => {
        process.exec(command, resolve)
    })
}